import logging
import time

from pysqlite2 import dbapi2 as sqlite

log = logging.getLogger('msgdb')

class FragEntry:
    def __init__(self):
        self.frags = {}

    def add_frag(self, msg_frag):
        if self.frags.has_key(msg_frag.seqno):
            log.warn('Duplicate seqno %d' % msg_frag.seqno)
        self.frags[msg_frag.seqno] = msg_frag

    def done(self):
        if not self.frags.has_key(0):
            return False
        return len(self.frags) == self.frags[0].num_frags

class MessageDB:
    def __init__(self, master, dbfile):
        self.master = master
        self.dbfile = dbfile
        self.connection = sqlite.connect(dbfile)
        
        c = self.connection.cursor()
        c.execute('SELECT name from SQLite_Master;')
        if len(c.fetchall()) == 0:
            log.info('creating SMS DB tables')
            c.execute('''CREATE TABLE sms_msgs (
                             id INTEGER,
                             recv_time,
                             sender,
                             uniq,
                             data);''')
            self.connection.commit()            
            self.id = 0
        else:
            c.execute('SELECT max(id) FROM sms_msgs;')
            r = c.fetchall()[0]
            if r[0] is None:
                self.id = 0
            else:
                self.id = int(r[0]) + 1
                
        self.frags = {}
        log.debug('sms database opened, id = %d' % (self.id))

    def get_id(self):
        i = self.id 
        self.id = self.id + 1
        return i

    def get_messages(self, delete = False, min_id = -1):
        c = self.connection.cursor()
        c.execute('SELECT * FROM sms_msgs WHERE id > ?', (min_id,))
        rows = c.fetchall()
        if delete:
            c.execute('DELETE FROM sms_msgs WHERE id > ?', (min_id,))
            self.connection.commit()
        return [dict(zip(['id', 'recv_time', 'number', 'sms_timestamp', 'text', 'raw_pdu'], r))
                for r in rows]

    def add_msg(self, sender_num, uniq, bytes):
        log.debug('%s %s %s' % (sender_num, uniq, bytes))
        c = self.connection.cursor()
        c.execute('INSERT INTO sms_msgs VALUES (?, ?, ?, ?, ?);',
                  (self.get_id(), time.time(), sender_num, uniq, bytes))
        self.connection.commit()

    def add_frag(self, sender_num, data):
        try:
            msg_frag = self.master.encoder.decode(data, sender_num)
        except Exception, e:
            log.error("Couldn't decode fragment %s, error was %s" % (data, e))
            return
        
        if msg_frag.num_frags == 1:
            self.add_msg(msg_frag.sender_num, msg_frag.uniq, msg_frag.bytes)
        else:
            if not self.frags.has_key(msg_frag.uniq):
                fe = FragEntry()
                self.frags[msg_frag.uniq] = fe
            else:
                fe = self.frags[msg_frag.uniq]
            fe.add_frag(msg_frag)
            if fe.done():
                try:
                    msg = self.master.encoder.merge(fe.frags.values())
                except proto.CorruptMessageError, e:
                    log.error("Corrupt message in the merge: %s" % e)
                    del self.frags[msg_frag.uniq]
                    return
                self.add_msg(msg_frag.sender_num, msg_frag.uniq, msg)

if __name__ == '__main__':
    import proto
    import pdu

    logging.basicConfig(level = logging.DEBUG)
     
    class M:
        pass
    
    m = M()
    m.encoder = proto.Encoder(159)
    msg_db = MessageDB(m, ':memory:')

    def test_frag(num, uniq, bytes):
        l = m.encoder.encode(uniq, bytes)
        for i in l:
            msg_db.add_frag(num, i)
    test_frag('15551231234', 'a', 'I am a walrus')
    test_frag('15551231234', 'b', 'I am a walrus' * 10)
    test_frag('15551231234', 'c', 'I am a walrus' * 100)
    test_frag('15551231234', 'd', '"XXX' + 'I am a walrus' * 150 + 'YYY"')
